metadata: serialize Resolve
authorAleksey Kladov <aleksey.kladov@gmail.com>
Mon, 21 Dec 2015 15:05:26 +0000 (18:05 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Mon, 25 Jan 2016 14:12:37 +0000 (17:12 +0300)
src/cargo/ops/cargo_output_metadata.rs
tests/test_cargo_metadata.rs

index 38f084af29453731ec46882354c3ea07ab378c68..6d4415e519056ff229e9091dc5be5a853ccc2539 100644 (file)
@@ -1,5 +1,4 @@
 use std::ascii::AsciiExt;
-use std::collections::HashMap;
 use std::path::{Path, PathBuf};
 
 use core::resolver::Resolve;
@@ -53,40 +52,13 @@ pub fn output_metadata(opt: OutputMetadataOptions, config: &Config) -> CargoResu
                                          config,
                                          opt.features,
                                          opt.no_default_features));
-    let (resolved_deps, packages) = deps;
+    let (packages, resolve) = deps;
 
-    #[derive(RustcEncodable)]
-    struct RootPackageInfo<'a> {
-        name: &'a str,
-        version: String,
-        features: Option<&'a HashMap<String, Vec<String>>>,
-    }
-
-    #[derive(RustcEncodable)]
-    struct ExportInfo<'a> {
-        root: RootPackageInfo<'a>,
-        packages: Vec<&'a Package>,
-    }
-
-    let mut output = ExportInfo {
-        root: RootPackageInfo {
-            name: resolved_deps.root().name(),
-            version: format!("{}", resolved_deps.root().version()),
-            features: None,
-        },
-        packages: Vec::new(),
+    let output = ExportInfo {
+        packages: &packages,
+        resolve: &resolve,
     };
 
-    for package in packages.iter() {
-        output.packages.push(&package);
-        if package.package_id() == resolved_deps.root() {
-            let features = package.manifest().summary().features();
-            if !features.is_empty() {
-                output.root.features = Some(features);
-            }
-        }
-    }
-
     let serialized_str = match &opt.output_format.to_ascii_uppercase()[..] {
         "TOML" => toml::encode_str(&output),
         "JSON" => try!(json::encode(&output)),
@@ -102,13 +74,20 @@ pub fn output_metadata(opt: OutputMetadataOptions, config: &Config) -> CargoResu
     Ok(())
 }
 
+#[derive(RustcEncodable)]
+struct ExportInfo<'a> {
+    packages: &'a [Package],
+    resolve: &'a Resolve,
+}
+
+
 /// Loads the manifest and resolves the dependencies of the project to the
 /// concrete used versions. Afterwards available overrides of dependencies are applied.
 fn resolve_dependencies(manifest: &Path,
                         config: &Config,
                         features: Vec<String>,
                         no_default_features: bool)
-                        -> CargoResult<(Resolve, Vec<Package>)> {
+                        -> CargoResult<(Vec<Package>, Resolve)> {
     let mut source = try!(PathSource::for_path(manifest.parent().unwrap(), config));
     try!(source.update());
 
@@ -122,5 +101,5 @@ fn resolve_dependencies(manifest: &Path,
 
     let (packages, resolve_with_overrides, _) = deps;
 
-    Ok((resolve_with_overrides, packages))
+    Ok((packages, resolve_with_overrides))
 }
index f491737f8423771dd43d799bd8c09463a7b295c0..7ae3df66995a11affff7476583cc98664022fe62 100644 (file)
@@ -2,6 +2,8 @@ use std::fs::File;
 use std::io::prelude::*;
 
 use hamcrest::{assert_that, existing_file, is, equal_to};
+use rustc_serialize::json::Json;
+use support::registry::Package;
 use support::{project, execs, basic_bin_manifest};
 
 
@@ -27,7 +29,11 @@ kind = ["bin"]
 name = "foo"
 src_path = "src[..]foo.rs"
 
-[root]
+[resolve]
+package = []
+
+[resolve.root]
+dependencies = []
 name = "foo"
 version = "0.5.0"
 
@@ -41,11 +47,6 @@ test!(cargo_metadata_simple_json {
 
     assert_that(p.cargo_process("metadata").arg("-f").arg("json"), execs().with_stdout(r#"
         {
-            "root": {
-                "name": "foo",
-                "version": "0.5.0",
-                "features": null
-            },
             "packages": [
                 {
                     "name": "foo",
@@ -65,10 +66,121 @@ test!(cargo_metadata_simple_json {
                     "features": {},
                     "manifest_path": "[..]Cargo.toml"
                 }
-            ]
+            ],
+            "resolve": {
+                "package": [],
+                "root": {
+                    "name": "foo",
+                    "version": "0.5.0",
+                    "source": null,
+                    "dependencies" : []
+                },
+                "metadata": null
+            }
         }"#.split_whitespace().collect::<String>()));
 });
 
+
+test!(cargo_metadata_with_deps {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = []
+            license = "MIT"
+            description = "foo"
+
+            [[bin]]
+            name = "foo"
+
+            [dependencies]
+            bar = "*"
+        "#);
+    Package::new("baz", "0.0.1").publish();
+    Package::new("bar", "0.0.1").dep("baz", "0.0.1").publish();
+
+    assert_that(p.cargo_process("metadata")
+                 .arg("--output-path").arg("metadata.json")
+                 .arg("-f").arg("json"),
+
+                 execs().with_status(0));
+
+    let outputfile = p.root().join("metadata.json");
+    assert_that(&outputfile, existing_file());
+
+    let mut output = String::new();
+    File::open(&outputfile).unwrap().read_to_string(&mut output).unwrap();
+    let result = Json::from_str(&output).unwrap();
+    println!("{}", result.pretty());
+
+    let packages = result.find("packages")
+                         .and_then(|o| o.as_array())
+                         .expect("no packages");
+
+    assert_that(packages.len(), is(equal_to(3)));
+
+    let root = result.find_path(&["resolve", "root"])
+                     .and_then(|o| o.as_object())
+                     .expect("no root");
+
+    // source is null because foo is root
+    let foo_id_start = format!("{} {}", root["name"].as_string().unwrap(),
+                                        root["version"].as_string().unwrap());
+    let foo_name = packages.iter().find(|o| {
+        o.find("id").and_then(|i| i.as_string()).unwrap()
+         .starts_with(&foo_id_start)
+    }).and_then(|p| p.find("name"))
+      .and_then(|n| n.as_string())
+      .expect("no root package");
+    assert_that(foo_name, is(equal_to("foo")));
+
+    let foo_deps = root["dependencies"].as_array().expect("no root deps");
+    assert_that(foo_deps.len(), is(equal_to(1)));
+
+    let bar = &foo_deps[0].as_string().expect("bad root dep");
+
+    let check_name_for_id = |id: &str, expected_name: &str| {
+        let name = packages.iter().find(|o| {
+            id == o.find("id").and_then(|i| i.as_string()).unwrap()
+        }).and_then(|p| p.find("name"))
+          .and_then(|n| n.as_string())
+          .expect(&format!("no {} in packages", expected_name));
+
+        assert_that(name, is(equal_to(expected_name)));
+    };
+
+    let find_deps = |id: &str| -> Vec<_> {
+        result.find_path(&["resolve", "package"])
+              .and_then(|o| o.as_array()).expect("resolve.package is not an array")
+              .iter()
+              .find(|o| {
+                  let o = o.as_object().expect("package is not an object");
+                  let o_id = format!("{} {} ({})",
+                                     o["name"].as_string().unwrap(),
+                                     o["version"].as_string().unwrap(),
+                                     o["source"].as_string().unwrap());
+                  id == o_id
+              })
+              .and_then(|o| o.find("dependencies"))
+              .and_then(|o| o.as_array())
+              .and_then(|a| a.iter()
+                             .map(|o| o.as_string())
+                             .collect())
+              .expect(&format!("no deps for {}", id))
+    };
+
+
+    check_name_for_id(bar, "bar");
+    let bar_deps = find_deps(&bar);
+    assert_that(bar_deps.len(), is(equal_to(1)));
+
+    let baz = &bar_deps[0];
+    check_name_for_id(baz, "baz");
+    let baz_deps = find_deps(&baz);
+    assert_that(baz_deps.len(), is(equal_to(0)));
+});
+
 test!(cargo_metadata_with_invalid_manifest {
     let p = project("foo")
             .file("Cargo.toml", "");